﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace ActionReporter
{
    /// <summary>
    /// This is the implementation of the IActionReporter interface. Whenever a report function is called, a respective
    /// event will be fired informing any listening components of the corresponding action. This allows any number of
    /// listening components to be informed of actions that Lisa is taking. Such components can include a GUI component
    /// which could display Lisa's actions or a Logger Component which could log all of Lisa's activities in a log file.
    /// </summary>
    public class ActionReporterImpl:IActionReporter
    {
        #region Events
        /// <summary>
        /// Fired when Lisa changed state.
        /// </summary>
        public event HandleAction StateChanged;

        /// <summary>
        /// Fired when Lisa executed a command.
        /// </summary>
        public event HandleAction CommandExecuted;

        /// <summary>
        /// Fires event when Lisa is asking for the user's permission
        /// </summary>
        public event HandleAction UserPermissionRequired;

        /// <summary>
        /// Fires event when Lisa needs to display some system info.
        /// </summary>
        public event HandleAction InfoUpdate;
        #endregion

        #region Methods implemented from IActionReporter Interface
        /// <summary>
        /// Reports a State change and allows listening components to find out about the state change.
        /// </summary>
        /// <param name="oldState">The old state.</param>
        /// <param name="newState">The new state to which Lisa is switching.</param>
        public void reportStateChange(String oldState, String newState)
        {
            FireStateChanged(new ActionEventArgs("Changing state from: " + oldState + " to " + newState));
        }

        /// <summary>
        /// Reports the execution of a command.
        /// </summary>
        /// <param name="command">The command that's being executed.</param>
        public void reportCommandExecution(String command)
        {
            FireCommandExecuted(new ActionEventArgs("Executing command: " + command));
        }

        /// <summary>
        /// Reports the requirement of the user's permission.
        /// </summary>
        /// <param name="information">Additional information about why the user's permission is required.</param>
        public void reportUserPermissionRequired(String information)
        {
            FireUserPermissionRequired(new ActionEventArgs("User Permission Required: " + information));
        }

        /// <summary>
        /// Reports general system information.
        /// </summary>
        /// <param name="information"></param>
        public void reportSystemInfo(String information)
        {
            FireInfoUpdate(new ActionEventArgs(information));
        }

        #endregion

        #region Event-related methods
        private void FireStateChanged(ActionEventArgs eventArgs)
        {
            // Get local event for thread safety purposes
            HandleAction handler = StateChanged;
            FireHandleActionEvent(handler, eventArgs);
        }

        private void FireCommandExecuted(ActionEventArgs eventArgs)
        {
            HandleAction handler = CommandExecuted;
            FireHandleActionEvent(handler, eventArgs);
        }

        private void FireUserPermissionRequired(ActionEventArgs eventArgs)
        {
            HandleAction handler = UserPermissionRequired;
            FireHandleActionEvent(handler, eventArgs);
        }

        private void FireInfoUpdate(ActionEventArgs eventArgs)
        {
            HandleAction handler = InfoUpdate;
            FireHandleActionEvent(handler, eventArgs);
        }

        private void FireHandleActionEvent(HandleAction handler, ActionEventArgs eventArgs)
        {
            if (handler != null)
            {
                foreach (HandleAction singleCast in handler.GetInvocationList())
                {
                    ISynchronizeInvoke syncInvoke = singleCast.Target as ISynchronizeInvoke;
                    try
                    {
                        if (syncInvoke != null && syncInvoke.InvokeRequired)
                        {
                            // Invokie the event on the main thread
                            syncInvoke.Invoke(handler, new object[] { eventArgs });
                        }
                        else
                        {
                            // Raise the event
                            singleCast(eventArgs);
                        }
                    }
                    catch
                    {
                    }
                }
            }
        }
        #endregion
    }
}
